
{**
@abstract(Construction des messages @html(<a href="https://www.shredderchess.com/chess-features/uci-universal-chess-interface.html">UCI</a>).)
} 
unit Uci;

interface

function MsgUci: string;
function MsgSetOption(const AName: string; const AValue: boolean): string;
function MsgNewGame: string;
function MsgIsReady: string;
function MsgPosition(const AFenPosition: string): string;
function MsgGo(const AMoveTime: integer): string;
function MsgStop: string;
function MsgQuit: string;

//function IsMsgId(const AMsg: string; out AName, AAuthor: string): boolean;
//function IsMsgOptions(const AMsg: string; out AOptChess960: boolean): boolean;
function IsMsgIdName(const AMsg: string; out AName: string): boolean;
function IsMsgIdAuthor(const AMsg: string; out AAuthor: string): boolean;
function IsMsgOption(const AMsg: string; out AOptionName: string): boolean;
function IsMsgUciOk(const AMsg: string): boolean;
function IsMsgBestMove(const AMsg: string; out ABestMove, APromotion: string): boolean;
function IsMsgReadyOk(const AMsg: string): boolean;
function IsMsgInfo(const AMsg: string): boolean;

implementation

uses
  SysUtils, RegExpr;

type
  TUciMessage = (
    ucUci,
    ucSetOption,
    ucNewGame,
    ucIsReady,
    ucPosition,
    ucGo,
    ucStop,
    ucQuit
  );

const
  CPatterns: array[TUciMessage] of string = (
    'uci',
    'setoption name %s value %s',
    'ucinewgame',
    'isready',
    'position fen %s',
    'go movetime %d',
    'stop',
    'quit'
  );

var
  LExprIdName, LExprIdAuthor, LExprOption: TRegExpr;

function MsgUci: string;
begin
  result := CPatterns[ucUci];
end;

function MsgSetOption(const AName: string; const AValue: boolean): string;
begin
  result := Format(CPatterns[ucSetOption], [AName, LowerCase(BoolToStr(AValue, TRUE))]);
end;

function MsgNewGame: string;
begin
  result := CPatterns[ucNewGame];
end;

function MsgIsReady: string;
begin
  result := CPatterns[ucIsReady];
end;

function MsgPosition(const AFenPosition: string): string;
begin
  result := Format(CPatterns[ucPosition], [AFenPosition]);
end;

function MsgGo(const AMoveTime: integer): string;
begin
  result := Format(CPatterns[ucGo], [AMoveTime]);
end;

function MsgStop: string;
begin
  result := CPatterns[ucStop];
end;

function MsgQuit: string;
begin
  result := CPatterns[ucQuit];
end;

function IsMsgIdName(const AMsg: string; out AName: string): boolean;
begin
  result := (Length(AMsg) > 0) and LExprIdName.Exec(AMsg);
  if result then
    AName := LExprIdName.Match[1];
end;

function IsMsgIdAuthor(const AMsg: string; out AAuthor: string): boolean;
begin
  result := (Length(AMsg) > 0) and LExprIdAuthor.Exec(AMsg);
  if result then
    AAuthor := LExprIdAuthor.Match[1];
end;

function IsMsgOption(const AMsg: string; out AOptionName: string): boolean;
begin
  result := (Length(AMsg) > 0) and LExprOption.Exec(AMsg);
  if result then
    AOptionName := LExprOption.Match[1];
end;

function IsMsgUciOk(const AMsg: string): boolean;
begin
  result := Pos('uciok', AMsg) > 0;
end;

function IsMsgBestMove(const AMsg: string; out ABestMove, APromotion: string): boolean;
const
  CPromoSymbols: set of char = ['n', 'b', 'r', 'q'];
var
  LPos: integer;
  LAux: string;
begin
  LPos := Pos('bestmove', AMsg);
  result := LPos = 1;
  if result then
  begin
    ABestMove := Copy(AMsg, LPos + 9, 4);
    LAux := Copy(AMsg, LPos + 13, 1);
    if (Length(LAux) = 1) and (LAux[1] in CPromoSymbols) then
      APromotion := LAux
    else
      APromotion := EmptyStr;
  end else
  begin
    ABestMove := EmptyStr;
    APromotion := EmptyStr;
  end;
end;

function IsMsgReadyOk(const AMsg: string): boolean;
begin
  result := Pos('readyok', AMsg) = 1;
end;

function IsMsgInfo(const AMsg: string): boolean;
var
  LPos: integer;
begin
  LPos := Pos('info ', AMsg);
  result := LPos = 1;
end;

initialization
  LExprIdName := TRegExpr.Create('id name (.+)');
  LExprIdAuthor := TRegExpr.Create('id author (.+)');
  LExprOption := TRegExpr.Create('option name (.+) type (\w+)');
  
finalization
  LExprIdName.Free;
  LExprIdAuthor.Free;
  LExprOption.Free;
  
end.
